﻿using System;
using System.Net;
using System.Windows;
using System.Text;
using System.IO;
using CarsRegistrationMobile.Models.JSON;

namespace CarsRegistrationMobile.Managers
{
    public delegate void GotLoginResult(JsonResultBase jsonResult, string login, string passHash);
    public delegate void GotResultFromServer(JsonResultBase jsonResult);
    public delegate void GotRequestResult(JsonRequestResult jsonResult);

    public class NetworkManager
    {
        #region State

        private string loginURL;
        private string registrationURL;
        private string newRequestURL;

        #endregion State

        #region Interface

        public NetworkManager()
        {

            this.loginURL = "http://localhost:58764/Mobile/Login";
            this.registrationURL = "http://localhost:58764/Mobile/Registration";
            this.newRequestURL = "http://localhost:58764/Mobile/CreateRequest";
        }

        public void BeginLogin(string login, string passHash, GotLoginResult callback)
        {
            string loginUrl = this.GetLoginURL(login, passHash);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(loginUrl);
            request.Method = "GET";

            AsyncCallback asyncCallback = (asyncResult) =>
            {
                try
                {
                    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
                    string jsonResult = string.Empty;
                    using (var stream = response.GetResponseStream())
                    {
                        byte[] resultData = new byte[stream.Length];

                        AsyncCallback readAsyncCallback = (readAsyncResult) =>
                        {
                            stream.EndRead(readAsyncResult);
                            jsonResult = Encoding.UTF8.GetString(resultData, 0, resultData.Length);
                            var storageManager = ((App)Application.Current).StorageManager;
                            JsonResultBase res = storageManager.GetJsonResult<JsonResultBase>(jsonResult);
                            callback(res, login, passHash);
                        };
                        stream.BeginRead(resultData, 0, (int)stream.Length, readAsyncCallback, null);
                    }
                }
                catch (Exception)
                {
                    callback(null, login, passHash);
                }
            };

            request.BeginGetResponse(asyncCallback, null);
        }
        public void BeginRegistration(string login, string passHash, string name,
                                      string lastName, string middleName,
                                      string passportSeries, string passportNumber,
                                      GotResultFromServer registrationCallback)
        {
            StringBuilder postData = new StringBuilder();
            postData.Append(string.Format("{0}={1}&", "login", login));
            postData.Append(string.Format("{0}={1}&", "passHash", passHash));
            postData.Append(string.Format("{0}={1}&", "firstName", name));
            postData.Append(string.Format("{0}={1}&", "lastName", lastName));
            postData.Append(string.Format("{0}={1}&", "middleName", middleName));
            postData.Append(string.Format("{0}={1}&", "passportSeries", passportSeries));
            postData.Append(string.Format("{0}={1}", "passportNumber", passportNumber));

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(this.registrationURL);
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            AsyncCallback gotResponseCallback = (responseAsyncResult) =>
            {
                try
                {
                    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(responseAsyncResult);
                    string requestResult = string.Empty;
                    using (var responceStream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(responceStream))
                        {
                            requestResult = reader.ReadToEnd();

                            var storageManager = ((App)Application.Current).StorageManager;
                            JsonResultBase jsonResult = storageManager.GetJsonResult<JsonResultBase>(requestResult);
                            registrationCallback(jsonResult);
                        }
                    }
                }
                catch (Exception e)
                {
                    registrationCallback(new JsonResultBase { ResultCode = -1, Result = "Custom error" });
                }
            };

            AsyncCallback gotStreamCallback = (streamAsyncResult) =>
            {
                using (var stream = request.EndGetRequestStream(streamAsyncResult))
                {
                    using (StreamWriter writer = new StreamWriter(stream))
                    {
                        writer.Write(postData.ToString());
                        writer.Flush();
                    }
                }

                request.BeginGetResponse(gotResponseCallback, null);
            };

            request.BeginGetRequestStream(gotStreamCallback, null);
        }

        public void BeginSendRequest(string ownerName, string vehicleNumber, GotRequestResult callback)
        {
            string url = this.GetNewRequestURL(ownerName, vehicleNumber);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";

            AsyncCallback gotResponseCallback = (asyncResult) =>
            {
                try
                {
                    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asyncResult);
                    string jsonResult = string.Empty;
                    using (var stream = response.GetResponseStream())
                    {
                        byte[] resultData = new byte[stream.Length];

                        AsyncCallback readAsyncCallback = (readAsyncResult) =>
                        {
                            stream.EndRead(readAsyncResult);
                            jsonResult = Encoding.UTF8.GetString(resultData, 0, resultData.Length);
                            var storageManager = ((App)Application.Current).StorageManager;
                            JsonRequestResult res = storageManager.GetJsonResult<JsonRequestResult>(jsonResult);
                            callback(res);
                        };
                        stream.BeginRead(resultData, 0, (int)stream.Length, readAsyncCallback, null);
                    }
                }
                catch (Exception e)
                {
                    callback(null);
                }
            };

            request.BeginGetResponse(gotResponseCallback, null);
        }

        public void BeginDeleteRequest()
        {
            var storageManager = ((App)Application.Current).StorageManager;
            string requestID = storageManager.GetSeting<string>("RequestID");

            string actionURL = "http://localhost:58764/Mobile/DeleteRequest";
            string url = string.Format("{0}?requestID={1}", actionURL, requestID);
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = "GET";
            request.BeginGetResponse((r) => request.EndGetResponse(r), null);
        }

        public void BeginSendImage(string requestID, Stream imageStream, bool first,
                                   bool last, GotResultFromServer callback)
        {
            var postBody = this.GetStringFromStream(imageStream);
            StringBuilder postData = new StringBuilder();
            postData.Append(string.Format("{0}={1}&", "requestID", requestID));
            postData.Append(string.Format("{0}={1}&", "imageBase64", postBody));
            postData.Append(string.Format("{0}={1}&", "first", first.ToString()));
            postData.Append(string.Format("{0}={1}&", "last", last.ToString()));

            HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://localhost:58764/Mobile/SaveImage");
            request.Method = "POST";
            request.ContentType = "application/x-www-form-urlencoded";

            AsyncCallback gotResponseCallback = (responseAsyncResult) =>
            {
                try
                {
                    HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(responseAsyncResult);
                    string requestResult = string.Empty;
                    using (var responceStream = response.GetResponseStream())
                    {
                        using (StreamReader reader = new StreamReader(responceStream))
                        {
                            requestResult = reader.ReadToEnd();

                            var storageManager = ((App)Application.Current).StorageManager;
                            JsonResultBase jsonResult = storageManager.GetJsonResult<JsonResultBase>(requestResult);
                            callback(jsonResult);
                        }
                    }
                }
                catch (Exception e)
                {
                    callback(new JsonResultBase { ResultCode = -1, Result = "Custom error" });
                }
            };

            AsyncCallback gotStreamCallback = (streamAsyncResult) =>
            {
                using (var stream = request.EndGetRequestStream(streamAsyncResult))
                {
                    using (StreamWriter writer = new StreamWriter(stream))
                    {
                        writer.Write(postData.ToString());
                        writer.Flush();
                    }
                }

                request.BeginGetResponse(gotResponseCallback, null);
            };

            request.BeginGetRequestStream(gotStreamCallback, null);
        }

        #endregion Interface

        #region Engine

        private string GetLoginURL(string login, string passHash)
        {
            return string.Format("{0}?login={1}&passHash={2}", this.loginURL, login, passHash);
        }
        private string GetNewRequestURL(string ownerName, string vehicleNumber)
        {
            string login = ((App)Application.Current).StorageManager.GetSeting<string>("Login");
            return string.Format("{0}?login={1}&ownerName={2}&vehicleNumber={3}", this.newRequestURL, login, ownerName, vehicleNumber);
        }

        private byte[] ReadToEnd(System.IO.Stream stream)
        {
            long originalPosition = stream.Position;
            stream.Position = 0;

            try
            {
                byte[] readBuffer = new byte[4096];

                int totalBytesRead = 0;
                int bytesRead;

                while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
                {
                    totalBytesRead += bytesRead;

                    if (totalBytesRead == readBuffer.Length)
                    {
                        int nextByte = stream.ReadByte();
                        if (nextByte != -1)
                        {
                            byte[] temp = new byte[readBuffer.Length * 2];
                            Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                            Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                            readBuffer = temp;
                            totalBytesRead++;
                        }
                    }
                }

                byte[] buffer = readBuffer;
                if (readBuffer.Length != totalBytesRead)
                {
                    buffer = new byte[totalBytesRead];
                    Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
                }
                return buffer;
            }
            finally
            {
                stream.Position = originalPosition;
            }
        }

        private string GetStringFromStream(Stream stream)
        {
            byte[] data = this.ReadToEnd(stream);
            var result = Convert.ToBase64String(data);

            return result;
        }

        #endregion Engine
    }
}
